﻿using Dapper;
using Dapper.Contrib.Extensions;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;

namespace Solution.Core.Repository
{
    public abstract class AbstractMysqlRepository
    {
        protected abstract string ConnString { get; }

        protected async Task<int> InsertAsync<T>(T entity, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "") where T : class
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.InsertAsync(entity, transaction, commandTimeout);
            }
        }

        protected async Task<bool> UpdateAsync<T>(T entity, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "") where T : class
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.UpdateAsync(entity, transaction, commandTimeout);
            }
        }

        protected async Task<bool> DeleteAsync<T>(T entity, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "") where T : class
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.DeleteAsync(entity, transaction, commandTimeout);
            }
        }

        protected async Task<T> GetAsync<T, TKey>(TKey id, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "") where T : class
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.GetAsync<T>(id, transaction, commandTimeout);
            }
        }

        protected async Task<int> ExecuteAsync(string sql, object param, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "")
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.ExecuteAsync(sql, param, transaction, commandTimeout);
            }
        }

        protected async Task<IEnumerable<T>> QueryAsync<T>(string sql, object param, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "")
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.QueryAsync<T>(sql, param, transaction, commandTimeout);
            }
        }

        protected async Task<T> ExecuteScalarAsync<T>(string sql, object param, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "")
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.ExecuteScalarAsync<T>(sql, param, transaction, commandTimeout);
            }
        }

        protected async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "")
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.QueryFirstOrDefaultAsync<T>(sql, param, transaction, commandTimeout);
            }
        }

        protected async Task<T> QuerySingleOrDefaultAsync<T>(string sql, object param, IDbTransaction transaction = null, int commandTimeout = 300, string connString = "")
        {
            using (var conn = GetMySqlConnection(connString))
            {
                return await conn.QuerySingleOrDefaultAsync<T>(sql, param, transaction, commandTimeout);
            }
        }

        private IDbConnection GetMySqlConnection(string connString)
        {
            if (string.IsNullOrEmpty(connString))
            {
                connString = ConnString;
                if (string.IsNullOrEmpty(connString))
                {
                    throw new ArgumentNullException(nameof(connString));
                }
            }

            return new MySqlConnection(connString);
        }
    }
}